//
// Created by Jisam on 2024/6/20.
//

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

/* 主函数 */
int main() {
    // 读取元素类型数量m与攻击序列长度n
    int n, m;
    cin >> m >> n;

    // 初始化一个m×m的矩阵，用于存储不同元素间反应造成的伤害
    vector<vector<int>> a(m, vector<int>(m));
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < m; j++) {
            cin >> a[i][j]; // 读取第i种元素与第j种元素反应的伤害值
        }
    }

    // 读取攻击序列
    string s;
    cin >> s;

    // 初始化nxt数组，用于记录每种元素在序列中下一次出现的位置信息
    vector<pair<int, int>> nxt(m);
    for (int i = 0; i < m; i++) {
        nxt[i] = {i, n}; // 初始化为当前位置和序列长度，表示未遇到该元素
    }

    // 初始化动态规划数组f，大小为2^m，用于存储各种元素激活/非激活状态下攻击序列的总伤害
    vector<i64> f(1 << m);

    // 逆序遍历攻击序列，计算所有可能的元素激活组合下的总伤害
    for (int i = n - 1; i >= 0; i--) {
        int x = s[i] - 'a'; // 当前要处理的元素在字母表中的位置
        int mask = 0; // 当前已处理的元素状态位

        // 遍历所有元素，更新动态规划数组f
        for (int j = 0; j < m; j++) {
            if (nxt[j].second == n) break; // 若该元素后续不再出现，跳过

            int y = nxt[j].first; // 下一个相同元素或n的位置
            // 根据当前元素状态更新f数组，考虑元素x与y的相互作用
            f[mask] += a[x][y];
            f[mask | (1 << x)] -= a[x][y];
            f[mask | (1 << y)] -= a[x][y];
            f[mask | (1 << x) | (1 << y)] += a[x][y];

            if (y == x) break; // 如果y就是x，无需继续寻找其他y
            mask |= (1 << y); // 更新已处理元素的状态位
        }

        // 更新nxt数组，将当前处理的元素移到队列最前面
        int j = 0;
        while (nxt[j].first != x) j++;
        nxt.erase(nxt.begin() + j);
        nxt.insert(nxt.begin(), make_pair(x, i));
    }

    // 使用位运算累加f数组，以合并不同元素激活情况下的总伤害
    for (int i = 1; i < (1 << m); i *= 2) {
        for (int j = 0; j < (1 << m); j += i * 2) {
            for (int k = 0; k < i; k++) {
                f[i + j + k] += f[j + k];
            }
        }
    }

    // 输出所有可能的元素激活组合下的总伤害
    for (int i = 0; i < (1 << m); i++) {
        cout << f[i] << " ";
    }
    return 0;
}
